

function _$size(el, w, h){
	el.style.width = w + "px";
	el.style.height = h + "px";
}
function _$move(el, x, y){
	el.style.top = x + "px";
	el.style.left = y + "px";
}
var TRANSLATE = "translate3d({0}px, {1}px, 0px) scale3d({2}, {2}, 1)";
function _$translate(el, x, y){
	var m = new WebKitCSSMatrix(el.style.webkitTransform);
	if(x===null || x===undefined)x=m.e;
	if(y===null || y===undefined)x=m.f;
	el.style.webkitTransform = _$subst(TRANSLATE, [x, y, m.a]);
	//e, f - x,y
	//a, d
}

function _$scale(el, scale){
	var m = new WebKitCSSMatrix(el.style.webkitTransform);
	el.style.webkitTransform = _$subst(TRANSLATE, [m.e, m.f, scale]);
}

//e, f - x,y	//a, d -scale
function _$wkitTrans(el){
	var m = new WebKitCSSMatrix(el.style.webkitTransform);
	return {
		x:m.e,
		y:m.f,
		zoom:m.a
	}
}

function _$strReverse(str){
	return str.split("").reverse().join("")	
}

function _$transform(el, x, y, scale){
	var m = new WebKitCSSMatrix(el.style.webkitTransform);
	el.style.webkitTransform = _$subst(TRANSLATE, [x, y, scale]);
}

function _$(query, parent){
	this.cache = this.cache || {};
	parent = parent || document;	
	var nQuery =  query.replace(/ /g,"_");
	if(this.cache[nQuery] && this.cache[nQuery].parent==parent){ 
		return this.cache[nQuery].result;
	}else{
		var result = parent.querySelector(query);	
		this.cache[nQuery] = {
			parent: parent,
			result : result
		};
		return result;
	}

}

function _$log() {
	var p = document.createElement("p");
	var message = Array.prototype.join.call(arguments, " ");
	p.innerHTML = message;
	document.getElementById("info").appendChild(p);
}

function traceObj(obj, msg){
	var logMsg = (msg) ? msg  : "";
	logMsg += "<<<<<<<<<<<<<<<<\n";
	for(var i in obj){
		if(obj[i] && obj[i].constructor == Function)continue;
		logMsg += i + "=>" + obj[i] + "\n";				
	}
	logMsg += ">>>>>>>>>>>>>>>\n";
	console.info(logMsg);
}

function _$assignDomShortcuts(){
	var scope = arguments[0];
	for(var i=1; i<arguments.length; i++){
		scope[arguments[i]] = _$("#" + arguments[i]);
	}
}



var DEFAULT_IMG_PRELOADER = "img/loading.gif";
function cssPreloadImage(el, url, preloaderUrl, bkgSize){
	this._cssImgCache = this._cssImgCache || {};

	if(_cssImgCache[url] ){
		el.style.backgroundRepeat = "no-repeat";
		el.style.backgroundPosition = "center";
		el.style.webkitBackgroundSize = (bkgSize!=null) ? bkgSize+"%" : "100%"; 
		el.style.backgroundImage = "url(" + url + ")";
		_cssImgCache[url] = 1;
		return; 
	}
	
	if(preloaderUrl){
		el.style.backgroundImage = "url(" + preloaderUrl + ")";
		el.style.backgroundRepeat = "no-repeat";
		el.style.backgroundPosition = "center";
		el.style.webkitBackgroundSize = "100% 100%";
	}else{		
		el.style.backgroundImage = "url(" + DEFAULT_IMG_PRELOADER + ")";
		el.style.backgroundRepeat = "no-repeat";
		el.style.backgroundPosition = "center";
		el.style.webkitBackgroundSize = "16px 16px";	
	}
	
	var img = new Image();
	img.addEventListener("load", 
		function(){
			el.style.webkitBackgroundSize = (bkgSize!=null) ? bkgSize+"%" : "100%";
			el.style.backgroundImage = "url(" + url + ")";
			_cssImgCache[url] = 1;
		}
	);
	
	img.src = url;	
	setTimeout(function(){
		if(! !!_cssImgCache[url]){
			console.error("LOAD FAIL->"+url);			
		}
	}, 1000);
	
}

var iRegex = /iP(hone|ad)/i;
function _$iPhone(){
	return iRegex.test(navigator.userAgent);
}
var ipadRegex = /iPad/i;
function _$iPad(){
	return ipadRegex.test(navigator.userAgent);
}

function _$fire(element, event, params){
    if (document.createEventObject){
        // dispatch for IE
        var evt = document.createEventObject();
        return element.fireEvent('on'+event,evt)
    }
    else{
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent(event, true, true ); // event type,bubbling,cancelable
        evt.params = params;
        return !element.dispatchEvent(evt);
    }
}




function _$anim(propName, el, duration, diff){
	var disp = document.createElement('SPAN');
	function bind(eventName, handler){
		disp.addEventListener(eventName, handler, false);
		return this;
	}
	
	function _stop(){
		el[propName] = startScroll+diff;
		clearInterval(iid);		
		
	}
	
	function _isRunning(){
		return __isRunning;	
	}	
	
	function _animate(){
		if(curStep<totalSteps){
			el[propName] +=  pxPerSec1;
			curStep++;
		}else{
			el[propName] = startScroll+diff;
			clearInterval(iid);		
			_$fire(disp, "evt_AnimationEnd", {propName:propName, el:el, duration:duration, diff:diff});
			__isRunning = false;
		}
	}	
	
	var timeout = 0.05;
	var pxPerSec = diff/duration;
	var pxPerSec1 = Math.floor(timeout * pxPerSec);
	var totalSteps = Math.floor(diff/pxPerSec1);	
	var startScroll = el[propName];
	var curStep = 1;
	var __isRunning = true;
	 
	_animate();
	var iid = setInterval(_animate,	timeout * 1000);

		

	return {
		stop: _stop,
		isRunning: _isRunning,
		bind: bind
	}
}

/*
 * var delta = maxValue - minValue; var stepp = minValue+(Math.pow(((1 /
 * totalSteps) * actualStep), powr) * delta); return Math.ceil(stepp)
 * 
 */

function _$yellowFade(el) {
	  var b = 155;
	  function f() {
	    el.style.background = 'rgb(100, 100,'+ (b-=4) +')';
	    if (b > 100) {
	      setTimeout(f, 40);
	    }
	  };
	  f();
}







var RUN_LATER_TIMEOUT = 100;
function _$runLater(fn) {
	setTimeout(fn, RUN_LATER_TIMEOUT);
}



function _$domHasCls(ele,cls) {
	return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}

function _$domAddCls(ele,cls) {
	if (!_$domHasCls(ele, cls)) ele.className = ele.className.replace(/\s*$/, " ") + cls;
}

function _$domRemCls(ele, cls) {
	var arr = cls.split(/\s/);
	var className = ele.className;
	for(var i=0; i<arr.length; i++){
		if (_$domHasCls(ele, arr[i]))className = className.replace(new RegExp('(\\s|^)'+arr[i]+'(\\s|$)'), ' ');
	}	
	ele.className=className;
}

function _$domToggleCls(el, cls){
	if(_$domHasCls(el, cls)){
		_$domRemCls(el, cls);
	 }else{
		_$domAddCls(el, cls);
	 }
}

function _$findFirst(el, touchSelectors, values) {
	var arr = (touchSelectors) ?  el.querySelectorAll(touchSelectors.join(', ')) : [el];
	var tmp = [values];
	if(values.constructor===Array)tmp=values;	
	for (var i=0; i<values.length; i++){
	for (var j=0; j<arr.length; j++){if (arr[j] == values[i]) return arr[j];	}
	}
	return null;
}

function _$domGetWithParents(targetNode) {
	var results = [];
	var curNode = targetNode;
	while(curNode){
		results.push(curNode);	
		curNode=curNode.parentNode;
	}
	return results;
}


function _$handleScroll(el){
	var startX, startY, startScrollX, startScrollY;
	el.addEventListener("touchstart", function(event){
		startScrollX = el.scrollLeft;
		startScrollY = el.scrollTop;
	    startX = event.changedTouches[0].clientX,
        startY = event.changedTouches[0].clientY,
		el.addEventListener("touchmove", _move);
		el.addEventListener("touchend", _end);
		console.info("START");
		function _move(event){
			el.scrollLeft = startScrollX + startX - event.changedTouches[0].clientX;
			el.scrollTop = startScrollY + startY - event.changedTouches[0].clientY;
			console.info("MOVE");
		}
		
		function _end(event){
			el.removeEventListener("touchmove", _move);
			el.removeEventListener("touchend", _end);
		}
		
	}, false);
}

var ID = 0;
function _$handleTouch(el, touchSelectors) {
	var disp = document.createElement('SPAN');
	function bind(eventName, handler){
		disp.addEventListener(eventName, handler, false);
		return this;
	}
	
	var _disabled;
	function disable(value){_disabled = value;	}
    function now(){	return (new Date()).getTime();   }
	
	
	if(el==null){
		return;		
	}
	if(!_$iPhone()){
		el.addEventListener("click",
			function(event){
				if(_disabled)return;			
				var targetElement = _$findFirst(el, touchSelectors, _$domGetWithParents(event.target));
				
				if(targetElement==null) return;
				 if(_$domHasCls(targetElement, "toggle")){
	 				 _$domToggleCls(targetElement, "selected");	
	 			 }
				 		
	 			 _$fire(disp, "evt_Tap", {targetEvent:event, targetElement: targetElement});
	 					
			}
		,false);
		
		el.addEventListener("dblclick",
				function(event){
					if(_disabled)return;			
					var targetElement = _$findFirst(el, touchSelectors, _$domGetWithParents(event.target));
					if(targetElement==null) return;
					_$fire(disp, "evt_DblTap", {targetEvent:event, targetElement: targetElement});
		 					
				}
		, false);
		
		var elid = ID++;
		el.addEventListener("mousedown", function(event){
					if(_disabled)return;
					var targetElement = _$findFirst(el, touchSelectors, _$domGetWithParents(event.target));
					if(targetElement==null) return;
			        var hoverTimeout = null,
		            startX = event.clientX,
		            startY = event.clientY,
		            deltaX = 0,
		            deltaY = 0,
		            startTime = now();
		
		            hoverTimeout = setTimeout(function(){ 
		            	_$domAddCls(targetElement, "active");          
		             }, 100);
		            _$fire(disp, "evt_TouchStart", {targetEvent:{touches:[event]}, targetElement: targetElement});
					
		
					el.addEventListener("mousemove", _move, false);
					window.addEventListener("mouseup",  _end, false);
		
					function _move(evt){
						_$fire(disp, "evt_TouchMove", {targetEvent:{touches:[evt]}, startX:startX, startY:startY, targetElement: targetElement});

					}
					
					function _end(){
						if(_disabled)return;
						window.removeEventListener("mouseup",  _end, false);
						window.removeEventListener("blur",  _end, false);
					 	el.removeEventListener("mousemove", _move);				
				 		 clearTimeout(hoverTimeout);
					 	_$fire(disp, "evt_TouchEnd");				
						_$domRemCls(targetElement, "active"); //temporary		

					}
					
	
				    return false;
		}, false);
	

	}else{
	
	var previousTapTime=0; 
	var TAP_TIME = 200;
	var DBL_TAP_TIME = 300;
	el.addEventListener("touchstart",
			function(event){	
				if(_disabled)return;
				var targetElement = _$findFirst(el, touchSelectors, _$domGetWithParents(event.target));
				if(targetElement==null) return;
		        var hoverTimeout = null,
	            startX = event.changedTouches[0].clientX,
	            startY = event.changedTouches[0].clientY,
	            deltaX = 0,
	            deltaY = 0,
	            startTime = now();
	
	            hoverTimeout = setTimeout(function(){ 
	            	_$domAddCls(targetElement, "active");          
	             }, 100);
	            
				_$fire(disp, "evt_TouchStart", {targetEvent:event, targetElement: targetElement});
				el.addEventListener("touchmove", _move, false);
				el.addEventListener("touchend",  _end, false);
				el.addEventListener("touchcancel",  _end, false);
				
									
				function _move(){
					_updateChanges();
					_$fire(disp, "evt_TouchMove", {targetEvent:event, startX:startX, startY:startY, targetElement: targetElement});
				}
				
				function _end(){
					if(_disabled)return;
					_updateChanges();
					 var curTime =  now();
					 var totalTime = curTime - startTime;
					 if(event.touches.length>1) return;
			 		 if (deltaY === 0 && deltaX === 0 && totalTime<=TAP_TIME){			
			 			 if(_$domHasCls(targetElement, "toggle")){
			 				 _$domToggleCls(targetElement, "selected");	
			 			 }
			 			 _$fire(disp, "evt_Tap", {targetEvent:event, targetElement: targetElement});
			 			 if( totalTime<TAP_TIME){
							 if(curTime-previousTapTime<=DBL_TAP_TIME){
								 _$fire(disp, "evt_DblTap", {targetEvent:event, targetElement: targetElement});
							 }
							 previousTapTime = curTime;
						 }
			 		 
			 		 }
				 
			 		 clearTimeout(hoverTimeout);
				 	_$fire(disp, "evt_TouchEnd");
				 	el.removeEventListener("touchmove", _move);
					el.removeEventListener("touchend",  _end);
					_$domRemCls(targetElement, "active"); //temporary
				}
				
			    function _updateChanges(){
			        var first = event.changedTouches[0] || null;
			        deltaX = first.pageX - startX;
			        deltaY = first.pageY - startY;			       
			    }
			    
	
						
			},
			true
	);
	
	}
	

	var result = {
		disp: disp,
		bind: bind,
		disable: disable
	}
	return result;

} 


// End touch handler

var MAX_REQ_TIME = 10;
function _$get(url, success, error, params){
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = __handleStatusChange;
	get_log("creating xhr");
	/*var xhrTimeout = setTimeout(function() {		
		xhr.abort();
		get_log("timeout: " + MAX_REQ_TIME*1000);
		error("timeout");
    }, MAX_REQ_TIME*1000);*/
	
	try{
		xhr.open("get", url, true);
		if(params)params= __urlParams(params);
		xhr.send(params);	
	}catch(e){
		get_log("Exception->"+e);
	}

	function get_log(msg){
		console.info("$get:" + msg);
	}
	
	function __handleStatusChange(){
		if(xhr.readyState!= 4){
			get_log("xhr state:"+xhr.readyState);
			return;
		}
		if (xhr.status != 200){
			error(xhr.status);
			/*	0: request not initialized 
				1: server connection established
				2: request received 
				3: processing request 
				4: request finished and response is ready
			 */
			get_log("http status is wrong:" + xhr.status);
		}
		
		//clearTimeout(xhrTimeout);
		get_log("xhr response");
		success(xhr.responseText, xhr);
	}

	function __urlParams(values) {
		var result="?";
		for(var key in values){
			result+=  encodeURIComponent(key) + "=" + encodeURIComponent(values[key])
		}
		return result;
	}
}


function _$urlGetParamByName( name )
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return decodeURIComponent(results[1].replace(/\+/g, " "));
}


var _$jsonParse=function(){var r="(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)",k='(?:[^\\0-\\x08\\x0a-\\x1f"\\\\]|\\\\(?:["/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';k='(?:"'+k+'*")';var s=new RegExp("(?:false|true|null|[\\{\\}\\[\\]]|"+r+"|"+k+")","g"),t=new RegExp("\\\\(?:([^u])|u(.{4}))","g"),u={'"':'"',"/":"/","\\":"\\",b:"\u0008",f:"\u000c",n:"\n",r:"\r",t:"\t"};function v(h,j,e){return j?u[j]:String.fromCharCode(parseInt(e,16))}var w=new String(""),x=Object.hasOwnProperty;return function(h,
j){h=h.match(s);var e,c=h[0],l=false;if("{"===c)e={};else if("["===c)e=[];else{e=[];l=true}for(var b,d=[e],m=1-l,y=h.length;m<y;++m){c=h[m];var a;switch(c.charCodeAt(0)){default:a=d[0];a[b||a.length]=+c;b=void 0;break;case 34:c=c.substring(1,c.length-1);if(c.indexOf("\\")!==-1)c=c.replace(t,v);a=d[0];if(!b)if(a instanceof Array)b=a.length;else{b=c||w;break}a[b]=c;b=void 0;break;case 91:a=d[0];d.unshift(a[b||a.length]=[]);b=void 0;break;case 93:d.shift();break;case 102:a=d[0];a[b||a.length]=false;
b=void 0;break;case 110:a=d[0];a[b||a.length]=null;b=void 0;break;case 116:a=d[0];a[b||a.length]=true;b=void 0;break;case 123:a=d[0];d.unshift(a[b||a.length]={});b=void 0;break;case 125:d.shift();break}}if(l){if(d.length!==1)throw new Error;e=e[0]}else if(d.length)throw new Error;if(j){var p=function(n,o){var f=n[o];if(f&&typeof f==="object"){var i=null;for(var g in f)if(x.call(f,g)&&f!==n){var q=p(f,g);if(q!==void 0)f[g]=q;else{i||(i=[]);i.push(g)}}if(i)for(g=i.length;--g>=0;)delete f[i[g]]}return j.call(n,
o,f)};e=p({"":e},"")}return e}}();

function _$subst(str, sub) {
	try{
	return str.replace(/\{(.+?)\}/g, function($0, $1) {
		return $1 in sub ? sub[$1] : $0;
	});
	}catch(e){
		console.error(sub);		
	}
};

function _$waitFor(callback, timeout){
	this.iid = this.iid || 0;
	if(this.iid ) return; 
	callback();
	this.iid = setTimeout(function(){
		this.iid = 0;
	}, timeout);
	return this;
}